package shotgun.my.sweetutil.redis;

//import com.broker.framework.common.exception.ServiceException;
//import com.broker.framework.common.exception.enums.GlobalErrorCodeConstants;
//import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;

//import static com.broker.framework.common.exception.util.ServiceExceptionUtil.exception;

/***
 * *---------------------------
 * @Description: redis分布式锁
 * ***---------------------------
 * @author: wulingming
 * @date: 2024年07月19日 11:13
 * @version: v1.0
 * ****---------------------------
 */
//@Slf4j
public abstract class AbstractRedisLockUtil<LockFlag> {

    /** 获取锁默认超时时间 */
    public static final int DEFAULT_TIMEOUT_MSECS = 10000;
    /** 锁默认失效时间 */
    public static final int DEFAULT_EXPIRE_MSECS = 60000;


    /**
     * *---------------------------
     * @Description: Lock and unlock
     * **---------------------------
     * @param lockKey the lock key
     * @param lockConsumer the lock consumer
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年07月19日 11:35
     * @version: v1.0
     * ****---------------------------
     */
    public void lockAndUnlock(String lockKey, Consumer<String> lockConsumer) {
        lockAndUnlock(lockKey, DEFAULT_TIMEOUT_MSECS, DEFAULT_EXPIRE_MSECS, lockConsumer);
    }

    /**
     * *---------------------------
     * @Description: Lock and unlock
     * **---------------------------
     * @param <R>  the type parameter
     * @param lockKey the lock key
     * @param lockFunction the lock function
     * @return r
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年07月19日 11:35
     * @version: v1.0
     * ****---------------------------
     */
    public <R> R lockAndUnlock(String lockKey, Function<String, R> lockFunction) {
        return lockAndUnlock(lockKey, DEFAULT_TIMEOUT_MSECS, DEFAULT_EXPIRE_MSECS, lockFunction);
    }

    /**
     * *---------------------------
     * @Description: Lock and unlock
     * **---------------------------
     * @param lockKey the lock key
     * @param timeoutMsecs the timeout msecs
     * @param lockConsumer the lock consumer
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年07月19日 11:35
     * @version: v1.0
     * ****---------------------------
     */
    public void lockAndUnlock(String lockKey, int timeoutMsecs, Consumer<String> lockConsumer) {
        lockAndUnlock(lockKey, timeoutMsecs, DEFAULT_EXPIRE_MSECS, lockConsumer);
    }

    /**
     * *---------------------------
     * @Description: Lock and unlock
     * **---------------------------
     * @param <R>  the type parameter
     * @param lockKey the lock key
     * @param timeoutMsecs the timeout msecs
     * @param lockFunction the lock function
     * @return r
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年07月19日 11:35
     * @version: v1.0
     * ****---------------------------
     */
    public <R> R lockAndUnlock(String lockKey, int timeoutMsecs, Function<String, R> lockFunction) {
        return lockAndUnlock(lockKey, timeoutMsecs, DEFAULT_EXPIRE_MSECS, lockFunction);
    }

    /**
     * *---------------------------
     * @Description: Lock and unlock
     * **---------------------------
     * @param lockKey the lock key
     * @param timeoutMsecs the timeout msecs
     * @param expireMsecs the expire msecs
     * @param lockConsumer the lock consumer
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年07月19日 11:35
     * @version: v1.0
     * ****---------------------------
     */
    public void lockAndUnlock(String lockKey, int timeoutMsecs, int expireMsecs, Consumer<String> lockConsumer) {
        lockAndUnlock(lockKey, timeoutMsecs, expireMsecs, s -> {
            lockConsumer.accept(s);
            return null;
        });
    }

    /**
     * *---------------------------
     * @Description: Lock and unlock
     * **---------------------------
     * @param <R>  the type parameter
     * @param lockKey the lock key
     * @param timeoutMsecs the timeout msecs
     * @param expireMsecs the expire msecs
     * @param lockFunction the lock function
     * @return r
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年07月19日 11:35
     * @version: v1.0
     * ****---------------------------
     */
    public <R> R lockAndUnlock(String lockKey, int timeoutMsecs, int expireMsecs, Function<String, R> lockFunction) {
        // 获取锁对象
        LockFlag lockFlag = getLockObj(lockKey);

        boolean isSuccessLock;
        try {
            //加锁
            // 尝试拿锁秒后停止重试,返回false,过期时间60秒
            isSuccessLock = tryLock(lockFlag, timeoutMsecs, expireMsecs, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            //TODO ...
//            log.error("redis分布式锁-加锁异常：Key:{},异常:", lockKey, e);
//            throw exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR);
            throw new RuntimeException();
        }

        if (!isSuccessLock) {
//            log.error("redis分布式锁-加锁失败：尝试拿锁{}ms后停止重试,过期时间{}ms,返回false,Key:{}", timeoutMsecs, expireMsecs, lockKey);
//            throw exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR);
            throw new RuntimeException();
        }

        try {
            //执行业务代码
            return lockFunction.apply(lockKey);
        }/* catch (ServiceException e) {
            log.error("redis分布式锁-执行业务代码异常（业务逻辑异常）：Key:{}", lockKey);
            throw e;
        }*/ catch (Exception e) {
//            log.error("redis分布式锁-执行业务代码异常（非业务逻辑异常）：Key:{}", lockKey);
            throw e;
        } finally {
            // 释放锁
            unlock(lockFlag);
        }
    }


    /**
     * *---------------------------
     * @Description: get lock obj.   获取lock锁对象，这里并未开始加锁，返回对象会在调用tryLock方法是传递过去
     * **---------------------------
     * @param lockKey the lock key
     * @return lock flag
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年09月14日 11:02
     * @version: v1.0
     * ****---------------------------
     */
    protected abstract LockFlag getLockObj(String lockKey);

    /**
     * *---------------------------
     * @Description: Try lock  尝试加锁， true：加锁成功 ； false：加锁失败。
     * **---------------------------
     * @param lockFlag the lock flag
     * @param timeoutMsecs the timeout msecs
     * @param expireMsecs the expire msecs
     * @param unit the unit
     * @return boolean
     * @throws InterruptedException the interrupted exception
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年09月14日 11:02
     * @version: v1.0
     * ****---------------------------
     */
    protected abstract boolean tryLock(LockFlag lockFlag, int timeoutMsecs, int expireMsecs, TimeUnit unit) throws InterruptedException;

    /**
     * *---------------------------
     * @Description: Unlock 释放锁
     * **---------------------------
     * @param lockFlag the lock flag
     * ***---------------------------
     * @author: wulingming
     * @date: 2024年09月14日 11:01
     * @version: v1.0
     * ****---------------------------
     */
    protected abstract void unlock(LockFlag lockFlag);


}